# 本程序由张道之创作
# 导入函数库
# -*- coding: utf-8 -*-
from datetime import datetime
from copy import copy
import numpy as np
import math

class BarData:
    def __init__(self, datetime, high_price, low_price, volume=0, open_interest=0, close_price=0, open_price=0,gap=0):
        """初始化属性"""
        self.high_price = high_price
        self.low_price = low_price
        self.datetime = datetime
        self.volume = volume
        self.open_interest = open_interest
        self.close_price = close_price
        self.open_price = open_price
        self.gap=gap  #缺口

class Chan_Strategy:

    def __init__(self, include=True, build_pivot=False):

        self.k_list = [] #原始k线
        self.k_macd_list=[] #原始k线macd指标[dif,dea,macd,datetime,ma_fast,ma_slow]
        self.chan_k_list = [] #缠论合并K线
        self.fx_list = [] #分型列表:[high_price, low, dt, direction, index of k_list],index of k_list是属于合并k线的索引
        self.stroke_list = [] #笔列表
        self.stroke_index_in_k = {}
        self.line_list = [] #线段列表
        self.line_index = {} #line_index:是一个字典，存放着线段端点对应着笔列表中的位置
        
        self.trend_list=[] #由线段组成的趋势端点列表，也是由分型构成
        self.trend_index={} #趋势索引，这是由线段列表中的位置组成
        
        self.stroke_pivot_of_line={}  #线段内部的笔中枢,中枢随着行情的增加会出现变化
        self.stroke_divergence={} #线段内部笔背驰情况
        self.line_pivot_of_trend={} #趋势内部的线段中枢
        self.line_divergence={} #趋势内部线段背驰情况
        
        self.stroke_macd={} #笔macd
        self.line_strength={} #线段强度
        self.stroke_strength={} #笔强度：笔斜率，笔macd柱子面积，黄白线极点
        
        self.buy_sell_signal={}
        # 笔生成方法，new, old
        # 是否进行K线包含处理
        self.include = include
        # 中枢生成方法，stroke, line
        # 使用笔还是线段作为中枢的构成, true使用线段
    
    def bar_has_gap(self,bar:BarData):
        """
        两根K线之间是否有缺口
        0:无缺口
        正值:上涨缺口
        负值：下跌缺口
        """
        if not isinstance(bar, BarData):
            raise TypeError("bar must be an instance of BarData")
        if not self.k_list:
            return 0
        
        last_bar = self.k_list[-1]
        last_high = last_bar.high_price
        last_low = last_bar.low_price
        if bar.high_price > last_high and bar.low_price > last_high:
            return abs(bar.low_price - last_high)
        elif bar.low_price < last_low and bar.high_price < last_low:
            return -abs(bar.high_price - last_low)
        else:
            return 0
    
    def on_kline_macd(self,data,fast,slow,period):
        """
        计算macd函数
        data:未合并K线
        """
        ma_fast=0 #快速均线
        ma_slow=0 #慢速均线
        dif=0
        dea=0
        macd=0
        if not self.k_macd_list:
            ma_fast=round(data[-1].close_price,2)
            ma_slow=ma_fast
            self.k_macd_list.append([dif,dea,macd,data[-1].datetime,ma_fast,ma_slow])
        else:
            last_macd=self.k_macd_list[-1]
            ma_fast=round((2*data[-1].close_price+(fast-1)*last_macd[4])/(fast+1),2)
            ma_slow=round((2*data[-1].close_price+(slow-1)*last_macd[5])/(slow+1),2)
            dif=round(ma_fast-ma_slow,2)
            dea=round((2*dif+(period-1)*last_macd[0])/(period+1),2)
            macd=round((dif-dea)*2,2)
            self.k_macd_list.append([dif,dea,macd,data[-1].datetime,ma_fast,ma_slow])    
            
    def on_bar(self, bar: BarData):
        
        print('++++++++++++++++++++++++',bar.datetime)
        bar.gap=self.bar_has_gap(bar) #计算缺口
        self.k_list.append(bar)
        self.on_kline_macd(self.k_list,12,26,9) #计算macd
        
        if self.include:
            self.on_process_k_include(bar) #合并K线

        else:
            self.on_process_k_no_include(bar)
            
    def on_process_k_include(self, bar: BarData):
        """
        合并k线
        """
        if len(self.chan_k_list) < 2:
            self.chan_k_list.append(bar)
            return
        
        cur_bar=copy(bar)
        new_bar=cur_bar
        last_bar=self.chan_k_list[-1]
        pre_bar = self.chan_k_list[-2]
        
        while len(self.chan_k_list)>1 and cur_bar.high_price >=last_bar.high_price and cur_bar.low_price <= last_bar.low_price: #最新K线包含前缠K:前包含
            self.chan_k_list.pop()
            chan_k=self.chan_k_list[::-1]
            for k in chan_k:
                if k.high_price>cur_bar.high_price: #下跌
                    new_bar.high_price = min(last_bar.high_price, cur_bar.high_price)
                    new_bar.low_price = min(last_bar.low_price, cur_bar.low_price)
                    cur_bar = new_bar
                    last_bar=self.chan_k_list[-1]
                    break
                elif k.low_price<cur_bar.low_price: #上涨
                    new_bar.high_price = max(last_bar.high_price, cur_bar.high_price)
                    new_bar.low_price = max(last_bar.low_price, cur_bar.low_price)
                    cur_bar = new_bar
                    last_bar=self.chan_k_list[-1]
                    break
            else: #循环完仍然不知上下
                new_bar.high_price = min(last_bar.high_price, cur_bar.high_price)
                new_bar.low_price = min(last_bar.low_price, cur_bar.low_price)
                cur_bar = new_bar
                
        if cur_bar.high_price <=last_bar.high_price and cur_bar.low_price >= last_bar.low_price: #最新K线被包含：后包含
            if self.chan_k_list:
                self.chan_k_list.pop()
#             new_bar=copy(last_bar)
            if last_bar.high_price>pre_bar.high_price:
                new_bar.high_price = max(last_bar.high_price, cur_bar.high_price)
                new_bar.low_price = max(last_bar.low_price, cur_bar.low_price)
                new_bar.datetime=last_bar.datetime
            else:
                new_bar.high_price = min(last_bar.high_price, cur_bar.high_price)
                new_bar.low_price = min(last_bar.low_price, cur_bar.low_price)
                new_bar.datetime=last_bar.datetime
        self.chan_k_list.append(new_bar)
        self.on_process_fx(self.chan_k_list)

    def on_process_k_no_include(self, bar: BarData):
        """不用合并k线"""
        self.chan_k_list.append(bar)
        self.on_process_fx(self.chan_k_list)
    
    def on_process_fx(self, data):
        """
        判断分型函数
        data:这里是缠论合并K线列表
        分型:[high_price, low, dt, direction, index of k_list],index of k_list是属于合并k线的索引
        """
        if len(data) > 2:
            flag = False
            if data[-2].high_price > data[-1].high_price and data[-2].high_price > data[-3].high_price:
                # 形成顶分型 
                self.fx_list.append([data[-2].high_price, data[-2].low_price, data[-2].datetime, 'up', len(data) - 2])
                flag = True

            if data[-2].low_price < data[-1].low_price and data[-2].low_price < data[-3].low_price:
                # 形成底分型
                self.fx_list.append([data[-2].high_price, data[-2].low_price, data[-2].datetime, 'down', len(data) - 2])
                flag = True

            if flag:
                print('分型成立，判断分笔')
                self.on_stroke(self.fx_list[-1])

    def is_stroke_extreme(self,cur_no_inc_ix,last_no_inc_ix,cur_fx):
        
        """
        判断某个分型是否是一笔内部的极值函数
        cur_no_inc_ix:当前未合并K线索引
        last_no_inc_ix:上一个分型索引
        """
        flag=1 #目前是极点
        for v in self.k_list[last_no_inc_ix:cur_no_inc_ix]:
            if (cur_fx[3]=='up'and v.high_price>cur_fx[0]) or (cur_fx[3]=='down'and v.low_price<cur_fx[1]):
                flag=0
                break
        return flag
    
    def stroke_has_gap(self,cur_no_inc_ix,last_no_inc_ix):
        """
        两分型之间是否有缺口
        """
        if last_no_inc_ix>=cur_no_inc_ix:

            return [0,0]
        
        for i  in range(last_no_inc_ix+1,cur_no_inc_ix+1):
            bar = self.k_list[i]
            if bar.gap!=0:
                return [i,bar.gap]
        
        return [0,0]
    
    def get_dt_index(self,dt):
        """
        获取某日期在原始未合并的k线的索引
        """
        index=0
        for index,bar in enumerate(self.k_list):
            if bar.datetime==dt:
                return index
            
    def on_stroke(self, data):
        """
        生成笔
        data:这里是一个分型数据，单根K线
        """
        if len(self.stroke_list) < 1:
            self.stroke_list.append(data)
        else:
            last_fx = self.stroke_list[-1]
            cur_fx = data
            cur_no_inc_ix=self.get_dt_index(cur_fx[2])
            last_no_inc_ix=self.get_dt_index(last_fx[2])
            gap=self.stroke_has_gap(cur_no_inc_ix,last_no_inc_ix)
            pivot_flag = False
            # 分型之间需要超过三根chank线
            # 延申也是需要条件的
            if last_fx[3] == cur_fx[3]:  #若分型方向相同

                if (last_fx[3] == 'down' and cur_fx[1] < last_fx[1]) or (
                        last_fx[3] == 'up' and cur_fx[0] > last_fx[0]):
                    # 笔延申

                    self.stroke_list[-1] = cur_fx
                    pivot_flag = True
                    # 修正倒数第二个分型是否是最高的顶分型或者是否是最低的底分型
                    start = -2
                    stroke_change = None
                    if len(self.stroke_list) > 1:
                        if cur_fx[3] == 'down':
                            while len(self.fx_list) > abs(start) and self.fx_list[start][2] > last_fx[2]:
                                if self.fx_list[start][3] == 'up' and self.fx_list[start][0] > self.stroke_list[-2][0]:
                                    stroke_change = self.fx_list[start]
                                start -= 1
                        else:
                            while len(self.fx_list) > abs(start) and self.fx_list[start][2] > last_fx[2]:
                                if self.fx_list[start][3] == 'down' and self.fx_list[start][1] < self.stroke_list[-2][
                                    1]:
                                    stroke_change = self.fx_list[start]
                                start -= 1
                    if stroke_change:
                        
                        self.stroke_list[-2] = stroke_change
                        if len(self.stroke_list) > 2:
                            
                            cur_fx = self.stroke_list[-2]
                            last_fx = self.stroke_list[-3]
                            self.stroke_strength[cur_fx[2]]=self.cal_stroke_strength(last_fx[2], cur_fx[2],cur_fx[3])
                        if cur_fx[4] - self.stroke_list[-2][4] < 4:
                            
                            self.stroke_list.pop()
                
            else:
                is_extreme=self.is_stroke_extreme(cur_no_inc_ix,last_no_inc_ix,cur_fx)
                stroke_gap=self.stroke_has_gap(cur_no_inc_ix,last_no_inc_ix)
                gap_pre_line = self.k_list[stroke_gap[0]]
                # if (cur_fx[4] - last_fx[4] > 3) and (
                #         (cur_fx[3] == 'down' and cur_fx[1] < last_fx[1] and cur_fx[0] < last_fx[0]) or (
                #         cur_fx[3] == 'up' and cur_fx[0] > last_fx[0] and cur_fx[1] > last_fx[1])):
                
                if (((cur_fx[4] - last_fx[4] >= 3 and
                     ((cur_no_inc_ix-last_no_inc_ix>=4 and is_extreme==1)or
                      (cur_no_inc_ix-last_no_inc_ix>=10 and is_extreme==0)))
                     
                     or (cur_no_inc_ix-last_no_inc_ix>=2 and is_extreme==1 and 
                         abs(stroke_gap[1])>abs(gap_pre_line.open_price - gap_pre_line.close_price))
                    )
                    and ((cur_fx[3] == 'down' and cur_fx[0] <= last_fx[1]) or 
                         (cur_fx[3] == 'up' and cur_fx[1] >= last_fx[0]))
                   ):
                    # 笔新增
                    self.stroke_list.append(cur_fx)
                    pivot_flag = True
            #划分线段
            self.on_line(self.stroke_list)
            if len(self.stroke_list) > 1:
                cur_fx = self.stroke_list[-1]
                last_fx = self.stroke_list[-2]
                #计算笔的力量强度
                self.stroke_strength[cur_fx[2]]=self.cal_stroke_strength(last_fx[2], cur_fx[2],cur_fx[3])
                #计算前线段端点到目前分型力量强度
                self.line_strength[cur_fx[2]]=self.cal_line_strength(self.line_list)
                #划分线段内笔中枢
                self.on_stroke_pivot_of_line(self.stroke_list)
                #划分趋势内部线段中枢
                self.on_line_pivot_of_trend(self.line_list)
                #判断线段内笔背驰
                self.stroke_divergence[cur_fx[2]]=self.on_stroke_divergence_signal(self.line_list)
                self.line_divergence[cur_fx[2]]=self.on_line_divergence_signal(self.trend_list)
                self.on_first_buy_sell(self.line_list)###################

    def on_line(self, data):
        """
        line_list保持和stroke_list结构相同，都是由分型构成的
        data:stroke_list
        line_index:是一个字典，存放着线段端点对应着笔列表中的位置
        """
        if len(data) > 4:

            if data[-1][3] == 'up' and data[-3][0] >= data[-1][0] and data[-3][0] >= data[-5][0]:
                if not self.line_list or self.line_list[-1][3] == 'down':
                    if (not self.line_list or ((len(self.stroke_list) - 3) - self.line_index[
                        self.line_list[-1][2]] >2 and data[-3][0]>self.line_list[-1][1])
                        ):
                        # 出现顶
                        self.line_list.append(data[-3])
                        self.line_index[self.line_list[-1][2]] = len(self.stroke_list) - 3
                    elif (len(self.line_list)>=3 and (len(self.stroke_list) - 3) - self.line_index[
                        self.line_list[-1][2]] ==1 and data[-3][0]>self.line_list[-2][0]):
                        #笔分型缺口，又遇到笔破坏
                        pre_line_index=self.line_index[self.line_list[-2][2]]
                        pre_stroke=self.stroke_list[pre_line_index-2]
                        if (self.line_list[-1][1]>pre_stroke[0] and self.line_index[
                            self.line_list[-1][2]] - self.line_index[
                            self.line_list[-2][2]] ==3):
                            self.line_list.pop()
                            self.line_list.pop()
                        self.line_list.append(data[-3])
                        self.line_index[self.line_list[-1][2]] = len(self.stroke_list) - 3
                else:
                    # 延申顶
                    if self.line_list[-1][0] < data[-3][0]:
                        self.line_list[-1] = data[-3]
                        self.line_index[self.line_list[-1][2]] = len(self.stroke_list) - 3
            if data[-1][3] == 'down' and data[-3][1] <= data[-1][1] and data[-3][1] <= data[-5][1]:
                if not self.line_list or self.line_list[-1][3] == 'up':
                    if (not self.line_list or((len(self.stroke_list) - 3) - self.line_index[
                        self.line_list[-1][2]] > 2 and data[-3][1]<self.line_list[-1][0])
                       ):
                        # 出现底
                        self.line_list.append(data[-3])
                        self.line_index[self.line_list[-1][2]] = len(self.stroke_list) - 3
                        
                    elif (len(self.line_list)>=3 and (len(self.stroke_list) - 3) - self.line_index[
                        self.line_list[-1][2]] ==1 and data[-3][1]<self.line_list[-2][1]):
                        pre_line_index=self.line_index[self.line_list[-2][2]]
                        pre_stroke=self.stroke_list[pre_line_index-2]
                        if (self.line_list[-1][0]<pre_stroke[1]and self.line_index[
                            self.line_list[-1][2]] - self.line_index[
                            self.line_list[-2][2]] ==3):
                            self.line_list.pop()
                            self.line_list.pop()
                        self.line_list.append(data[-3])
                        self.line_index[self.line_list[-1][2]] = len(self.stroke_list) - 3
                else:
                    # 延申底
                    if self.line_list[-1][1] > data[-3][1]:
                        self.line_list[-1] = data[-3]
                        self.line_index[self.line_list[-1][2]] = len(self.stroke_list) - 3
        #划分趋势                
        self.on_trend(self.line_list)
    
    
    def on_trend(self,data):
        """
        trend_list保持和line_list结构相同，都是由分型构成的
        data:line_list线段列表
        trend_index:是一个字典，存放着线段端点对应着笔列表中的位置
        """
        if len(data) > 4:

            if data[-1][3] == 'up' and data[-3][0] >= data[-1][0] and data[-3][0] >= data[-5][0]:
                if not self.trend_list or self.trend_list[-1][3] == 'down':
                    if (not self.trend_list or ((len(self.line_list) - 3) - self.trend_index[
                        self.trend_list[-1][2]] >2 and data[-3][0]>self.trend_list[-1][1])
                        ):
                        # 出现顶
                        self.trend_list.append(data[-3])
                        self.trend_index[self.trend_list[-1][2]] = len(self.line_list) - 3
                    elif (len(self.trend_list)>=3 and (len(self.line_list) - 3) - self.trend_index[
                        self.trend_list[-1][2]] ==1 and data[-3][0]>self.trend_list[-2][0]):
                        #笔分型缺口，又遇到笔破坏
                        pre_line_index=self.trend_index[self.trend_list[-2][2]]
                        pre_line=self.line_list[pre_line_index-2]
                        if (self.trend_list[-1][1]>pre_line[0] and self.trend_index[
                            self.trend_list[-1][2]] - self.trend_index[
                            self.trend_list[-2][2]] ==3):
                            self.trend_list.pop()
                            self.trend_list.pop()
                        self.trend_list.append(data[-3])
                        self.trend_index[self.trend_list[-1][2]] = len(self.line_list) - 3
                else:
                    # 延申顶
                    if self.trend_list[-1][0] < data[-3][0]:
                        self.trend_list[-1] = data[-3]
                        self.trend_index[self.trend_list[-1][2]] = len(self.line_list) - 3
            if data[-1][3] == 'down' and data[-3][1] <= data[-1][1] and data[-3][1] <= data[-5][1]:
                if not self.trend_list or self.trend_list[-1][3] == 'up':
                    if (not self.trend_list or((len(self.line_list) - 3) - self.trend_index[
                        self.trend_list[-1][2]] > 2 and data[-3][1]<self.trend_list[-1][0])
                       ):
                        # 出现底
                        self.trend_list.append(data[-3])
                        self.trend_index[self.trend_list[-1][2]] = len(self.line_list) - 3
                        
                    elif (len(self.trend_list)>=3 and (len(self.line_list) - 3) - self.trend_index[
                        self.trend_list[-1][2]] ==1 and data[-3][1]<self.trend_list[-2][1]):
                        pre_line_index=self.trend_index[self.trend_list[-2][2]]
                        pre_line=self.line_list[pre_line_index-2]
                        if (self.trend_list[-1][0]<pre_line[1]and self.trend_index[
                            self.trend_list[-1][2]] - self.trend_index[
                            self.trend_list[-2][2]] ==3):
                            self.trend_list.pop()
                            self.trend_list.pop()
                        self.trend_list.append(data[-3])
                        self.trend_index[self.trend_list[-1][2]] = len(self.line_list) - 3
                else:
                    # 延申底
                    if self.trend_list[-1][1] > data[-3][1]:
                        self.trend_list[-1] = data[-3]
                        self.trend_index[self.trend_list[-1][2]] = len(self.line_list) - 3
                        
    
    
    
    #划分中枢，这里因为需要高级别，本级别以及低级别联立判断，所以这里需要划分笔中枢和线段中枢
    #两者划分方法一致，所以两个函数的形态基本一样，只需要将内部特有的变量改一下即可
    
    def on_stroke_pivot_of_line(self,data):
        """
        线段内部中枢划分函数
        分型列表:[high_price, low, dt, direction, index of k_list],index of k_list是属于合并k线的索引
        data:分笔数据
        self.stroke_pivot_of_line:存储字典
        中枢列表：[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]
        笔会变动，所以还要判断是否已经进行过中枢划分
        """
        if not self.line_list: #若无线段端点，则直接返回
            return 
        line_length=len(self.line_list)
        line_point=line_length-1
        line_point_fx=self.line_list[line_point] #线段端点
        line_index=self.line_index[line_point_fx[2]] #线段端点在分笔列表中的索引序号
        cur_fx=data[-2] #当前分型(前一个分型，主要是当前分型不稳定，后期会随着行情变动而变动)
        i=len(data)-2 #当前分型在分笔列表序号
        
        if data[-2][3]=='down':
            ZG = min(data[i-1][0], data[i-3][0])
            ZD = max(data[i][1], data[i-2][1])
            GG = max(data[i-1][0], data[i-3][0])
            DD = min(data[i][1], data[i-2][1])
        else:
            ZG = min(data[i][0], data[i-2][0])
            ZD = max(data[i-1][1], data[i-3][1])
            GG = max(data[i][0], data[i-2][0]) 
            DD = min(data[i-1][1], data[i-3][1])
            
        if ((data[i+1][3]==line_point_fx[3]=='up' and data[i+1][0]>line_point_fx[0]) or
            (data[i+1][3]==line_point_fx[3]=='down' and data[i+1][1]<line_point_fx[1])):   
            #当前分型前一分型i-1与线段端点相同，而且高低点还超过了线段端点，那么取的线段端点要向前移一位
            
            line_point=line_point-1
            line_point_fx=self.line_list[line_point] 
            line_index=self.line_index[line_point_fx[2]] 
                
        if i-line_index<4: #以旧线段端点划分中枢
            
            if len(self.line_list)>=2: #如果线段端点大于两个
                if((data[i][3]=='down' and data[i][1]>self.line_list[-2][1])
                   or(data[i][3]=='up'and data[i][0]<self.line_list[-2][0])):
                    line_point=line_point-1
                    line_point_fx=self.line_list[line_point] 
                    line_index=self.line_index[line_point_fx[2]] 
                else:
                    return 
            else:
                return

        if i-line_index==4: #新线段端点开始划分中枢 ，这里不能用elif，因为前面线段端点移动之后还是要判断一下
            
            if ((line_point_fx=='down' and line_point_fx[1]>DD) or
                (line_point_fx=='up' and line_point_fx[0]<GG)):
                return
            else:
                self.stroke_pivot_of_line[line_point_fx[2]]=[]
        
        

        if data[i][3]==line_point_fx[3] and i-line_index>=4: #新线段端点开始划分中枢
            
            pivot_list=self.stroke_pivot_of_line.get(line_point_fx[2]) #线段内笔中枢列表
            if not pivot_list: #如果中枢列表为空
                
                if ((line_point_fx[1]<data[i][1]<=data[i-3][0] and data[i][3]=='down')
                        or (line_point_fx[0]>data[i][0]>=data[i-3][1] and data[i][3]=='up')):
                        start_dt=data[i-3][2]
                        end_dt=data[i][2]
                        start_ix=i-3
                        end_ix=i
                        pivot_length=end_ix-start_ix
                        pivot=[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]
                        if pivot_list==None:
                            self.stroke_pivot_of_line[line_point_fx[2]]=[]
                            pivot_list=self.stroke_pivot_of_line.get(line_point_fx[2])
                        pivot_list.append(pivot)
            
            else: #中枢列表不为空
                
                last_pivot=pivot_list[-1]
                if i-last_pivot[1]==2:

                    if (((data[i][1]>last_pivot[4] or data[i-1][0]<last_pivot[2]) and data[i][3]=='down') or  #笔不相交中枢
                        ((data[i][0]<last_pivot[5] or data[i-1][1]<last_pivot[3] )and data[i][3]=='up')): 
                        #当前分型在中枢的上下两边不想交
                        return 
                    elif ((data[i][1]<=last_pivot[2] and data[i][3]=='down') or #笔低点低于中枢高点
                            (data[i][0]>=last_pivot[3] and data[i][3]=='up')):
                        # '相交与中枢高点'
                        start_dt=last_pivot[6]
                        end_dt=data[i][2]
                        start_ix=last_pivot[0]
                        end_ix=i
                        pivot_length=end_ix-start_ix
                        pivot_list[-1]=[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]
                    elif ((last_pivot[4]>data[i][1]>last_pivot[2] and 
                           data[last_pivot[1]-1][0]>data[last_pivot[0]][0] and data[i][3]=='down')
                          or
                          (last_pivot[5]>data[i][0]>last_pivot[3] and 
                           data[last_pivot[1]-1][1]<data[last_pivot[0]][1] and data[i][3]=='up')) : 
                        #笔低于中枢高高点，高于中枢高点且中枢高高点在中枢高点之后
                        # '相交与中枢高高点'
                        start_dt=data[i-3][2]
                        end_dt=data[i][2]
                        start_ix=i-3
                        end_ix=i
                        pivot_length=end_ix-start_ix
                        pivot_list[-1]=[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]
                    
                elif i-last_pivot[1]>2: #已经出现过三类买卖点
                    # '已有3买'
                    if ((data[i][1]<=data[i-3][0] and data[i][3]=='down')
                        or (data[i][0]>=data[i-3][1] and data[i][3]=='up')):
                        start_dt=data[i-3][2]
                        end_dt=data[i][2]
                        start_ix=i-3
                        end_ix=i
                        pivot_length=end_ix-start_ix
                        #         #判断更高级别中枢
                        if (((data[i][3]=='down' and DD<pivot_list[-1][2]) or
                            (data[i][3]=='up' and GG>pivot_list[-1][3])) 
                            and  i-last_pivot[1]>5 and last_pivot[-1]<9):
                            #两低级别中枢相交且9段以上
                            start_dt=pivot_list[-1][6]
                            end_dt=data[i][2]
                            start_ix=pivot_list[-1][0]
                            end_ix=i
                            pivot_length=end_ix-start_ix
                            ZG=min(GG,last_pivot[4])
                            ZD=max(DD,last_pivot[5])
                            GG=max(GG,last_pivot[4])
                            DD=min(DD,last_pivot[5])
                        pivot=[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]    
                        pivot_list.append(pivot)
    
    def line_mode(self,data):
        """
        判断一个线段是什么类型，线段由笔构成，而一个线段内部笔可以构成高级别，本级别，和低级别
        每个级别都有有种可能，一是趋势，二是盘整，所以总共有六种可能
        """
        cur_fx=self.stroke_list[-1]
        
                        
    def on_line_pivot_of_trend(self,data):
        """
        线段内部中枢划分函数
        分型列表:[high_price, low, dt, direction, index of k_list],index of k_list是属于合并k线的索引
        data:线段数据
        self.line_pivot_of_trend:存储中枢字典
        中枢列表：[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]
        笔会变动，所以还要判断是否已经进行过中枢划分
        """
        if not self.trend_list: #若无线段端点，则直接返回
            return 
        trend_length=len(self.trend_list)
        trend_point=trend_length-1
        trend_point_fx=self.trend_list[trend_point] #线段端点
        trend_index=self.trend_index[trend_point_fx[2]] #线段端点在分笔列表中的索引序号
        cur_fx=data[-2] #当前分型(前一个分型，主要是当前分型不稳定，后期会随着行情变动而变动)
        i=len(data)-2 #当前分型在分笔列表序号
        
        if data[-2][3]=='down':
            ZG = min(data[i-1][0], data[i-3][0])
            ZD = max(data[i][1], data[i-2][1])
            GG = max(data[i-1][0], data[i-3][0])
            DD = min(data[i][1], data[i-2][1])
        else:
            ZG = min(data[i][0], data[i-2][0])
            ZD = max(data[i-1][1], data[i-3][1])
            GG = max(data[i][0], data[i-2][0]) 
            DD = min(data[i-1][1], data[i-3][1])
            
        if ((data[i+1][3]==trend_point_fx[3]=='up' and data[i+1][0]>trend_point_fx[0]) or
            (data[i+1][3]==trend_point_fx[3]=='down' and data[i+1][1]<trend_point_fx[1])):   
            #当前分型前一分型i-1与线段端点相同，而且高低点还超过了线段端点，那么取的线段端点要向前移一位
            
            trend_point=trend_point-1
            trend_point_fx=self.trend_list[trend_point] 
            trend_index=self.trend_index[trend_point_fx[2]] 
                
        if i-trend_index<4: #以旧线段端点划分中枢
            
            if len(self.trend_list)>=2: #如果线段端点大于两个
                if((data[i][3]=='down' and data[i][1]>self.trend_list[-2][1])
                   or(data[i][3]=='up'and data[i][0]<self.trend_list[-2][0])):
                    trend_point=trend_point-1
                    trend_point_fx=self.trend_list[trend_point] 
                    trend_index=self.trend_index[trend_point_fx[2]] 
                else:
                    return 
            else:
                return

        if i-trend_index==4: #新线段端点开始划分中枢 ，这里不能用elif，因为前面线段端点移动之后还是要判断一下
            
            if ((trend_point_fx=='down' and trend_point_fx[1]>DD) or
                (trend_point_fx=='up' and trend_point_fx[0]<GG)):
                return
            else:
                self.line_pivot_of_trend[trend_point_fx[2]]=[]
        
        

        if data[i][3]==trend_point_fx[3] and i-trend_index>=4: #新线段端点开始划分中枢
            
            pivot_list=self.line_pivot_of_trend.get(trend_point_fx[2]) #线段内笔中枢列表
            if not pivot_list: #如果中枢列表为空
                
                if ((trend_point_fx[1]<data[i][1]<=data[i-3][0] and data[i][3]=='down')
                        or (trend_point_fx[0]>data[i][0]>=data[i-3][1] and data[i][3]=='up')):
                        start_dt=data[i-3][2]
                        end_dt=data[i][2]
                        start_ix=i-3
                        end_ix=i
                        pivot_length=end_ix-start_ix
                        pivot=[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]
                        if pivot_list==None:
                            self.line_pivot_of_trend[trend_point_fx[2]]=[]
                            pivot_list=self.line_pivot_of_trend.get(trend_point_fx[2]) 
                        pivot_list.append(pivot)
            
            else: #中枢列表不为空
                
                last_pivot=pivot_list[-1]
                if i-last_pivot[1]==2:

                    if (((data[i][1]>last_pivot[4] or data[i-1][0]<last_pivot[2]) and data[i][3]=='down') or  #笔不相交中枢
                        ((data[i][0]<last_pivot[5] or data[i-1][1]<last_pivot[3] )and data[i][3]=='up')): 
                        #当前分型在中枢的上下两边不想交
                        return 
                    elif ((data[i][1]<=last_pivot[2] and data[i][3]=='down') or #笔低点低于中枢高点
                            (data[i][0]>=last_pivot[3] and data[i][3]=='up')):
                        # '相交与中枢高点'
                        start_dt=last_pivot[6]
                        end_dt=data[i][2]
                        start_ix=last_pivot[0]
                        end_ix=i
                        pivot_length=end_ix-start_ix
                        pivot_list[-1]=[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]
                    elif ((last_pivot[4]>data[i][1]>last_pivot[2] and 
                           data[last_pivot[1]-1][0]>data[last_pivot[0]][0] and data[i][3]=='down')
                          or
                          (last_pivot[5]>data[i][0]>last_pivot[3] and 
                           data[last_pivot[1]-1][1]<data[last_pivot[0]][1] and data[i][3]=='up')) : 
                        #笔低于中枢高高点，高于中枢高点且中枢高高点在中枢高点之后
                        # '相交与中枢高高点'
                        start_dt=data[i-3][2]
                        end_dt=data[i][2]
                        start_ix=i-3
                        end_ix=i
                        pivot_length=end_ix-start_ix
                        pivot_list[-1]=[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]
                    
                elif i-last_pivot[1]>2: #已经出现过三类买卖点
                    # '已有3买'
                    if ((data[i][1]<=data[i-3][0] and data[i][3]=='down')
                        or (data[i][0]>=data[i-3][1] and data[i][3]=='up')):
                        start_dt=data[i-3][2]
                        end_dt=data[i][2]
                        start_ix=i-3
                        end_ix=i
                        pivot_length=end_ix-start_ix
                        #         #判断更高级别中枢
                        if (((data[i][3]=='down' and DD<pivot_list[-1][2]) or
                            (data[i][3]=='up' and GG>pivot_list[-1][3])) 
                            and  i-last_pivot[1]>5 and last_pivot[-1]<9):
                            #两低级别中枢相交且9段以上
                            start_dt=pivot_list[-1][6]
                            end_dt=data[i][2]
                            start_ix=pivot_list[-1][0]
                            end_ix=i
                            pivot_length=end_ix-start_ix
                            ZG=min(GG,last_pivot[4])
                            ZD=max(DD,last_pivot[5])
                            GG=max(GG,last_pivot[4])
                            DD=min(DD,last_pivot[5])
                        pivot=[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]    
                        pivot_list.append(pivot)                
    
    #要进行背驰的判断，首先就需要计算两个比较段之间的力度，而力度主要有两个构成
    #1.MACD面积，2.线段走势的角度和长度
    #所以需要计算笔和线段的力度
    def cal_stroke_strength(self,start_dt,end_dt,pattern):
        """
        笔强度函数：macd柱子面积，黄白线高度，笔斜率
        data:stroke_list
        pattern:'up' or 'down'
        返回强度数据列表[类型，macd面积，macd白线高度，笔斜率，笔长度]
        """
        start=self.get_dt_index(start_dt) #原始K线开始索引
        end=self.get_dt_index(end_dt) #原始K线结束索引
        summ = 0 #macd和
        if start >= end:
            return [pattern,summ,0,0]

        for i, v in enumerate(self.k_macd_list):
            if start <= i <= end:
                if pattern=='up' and v[2]>=0: #macd是红柱子
                    summ +=round(v[2], 4)
                elif pattern=='down' and v[2]<0: #macd是绿柱子
                    summ +=abs(round(v[2], 4))
        #计算未来的大约macd面积            
        if pattern=='up':
            endpoint=self.k_list[end].high_price
            startpoint=self.k_list[start].low_price
            if self.k_macd_list[end][2]<0:
                addition=0
            elif self.k_macd_list[end][2]>=self.k_macd_list[end-1][2]>=self.k_macd_list[end-2][2]:
                addition=summ
            else:
                addition=2*self.k_macd_list[end][2]
        else:
            endpoint=self.k_list[end].low_price
            startpoint=self.k_list[start].high_price
            if self.k_macd_list[end][2]>0:
                addition=0
            elif self.k_macd_list[end][2]<=self.k_macd_list[end-1][2]<=self.k_macd_list[end-2][2]:
                addition=summ
            else:
                addition=2*abs(self.k_macd_list[end][2])
                
        line_length=round(math.hypot(abs(endpoint-startpoint),end-start),2)
        line_gradient=round(math.degrees(math.atan(abs((end-start)/(endpoint-startpoint)))),2)
        return [pattern,round(summ+addition,2),abs(self.k_macd_list[end][0]),line_gradient,line_length]
    
    def cal_line_strength(self,data):
        """
        线段划分后判断
        data:线段列表
        还是有问题，主要是因为线段的划分属于动态的，有可能某个fenxin
        返回[分型类型，macd面积，线段斜率，线段长度]

        """
        if  not data:
            return 
        cur_fx=self.stroke_list[-1]
        pre_fx=self.stroke_list[-2]
        line_point_fx=data[-1]
        cur_fx_ix=self.get_dt_index(cur_fx[2])
        line_point_fx_ix=self.get_dt_index(line_point_fx[2])
        macd=0
        if ((cur_fx[3]==line_point_fx[3]=='up' and cur_fx[0]>line_point_fx[0]) or 
            (cur_fx[3]==line_point_fx[3]=='down' and cur_fx[1]<line_point_fx[1])):
            #若当前分型与线段列表中最后一个端点类相同且创新低或新高，则取线段列表中倒数第二个短点
            if len(data)<2:
                return
            line_point_fx=data[-2]
            line_point_fx_ix=self.get_dt_index(line_point_fx[2])
        if cur_fx[3]!=line_point_fx[3] and pre_fx[2]!=line_point_fx[2]: #分型不一样
            
            for m in self.k_macd_list[line_point_fx_ix:cur_fx_ix]:
                if cur_fx[3]=='up' and m[2]>0:
                    macd+=m[2]
                    
                elif cur_fx[3]=='down' and m[2]<=0:
                    macd+=abs(m[2])
                    
        if cur_fx[3]=='up':
            line_gradient=math.degrees(math.atan((cur_fx_ix-line_point_fx_ix)/(cur_fx[0]-line_point_fx[1])))
            line_length=math.hypot(cur_fx[0]-line_point_fx[1],cur_fx_ix-line_point_fx_ix)
        else:
            line_gradient=math.degrees(math.atan((cur_fx_ix-line_point_fx_ix))/(line_point_fx[1]-cur_fx[0]))
            line_length=math.hypot(line_point_fx[1]-cur_fx[0],cur_fx_ix-line_point_fx_ix)
        return [cur_fx[3],round(macd,2),round(line_gradient,2),round(line_length,2)]
    
    
    def cal_strokes_macd(self,start_ix,end_ix):
        """
        计算线段内部中枢连接段macd面积和斜率
        data:stroke_list
        pattern:'up' or 'down'
        返回强度数据列表[类型，macd面积，macd白线高度，笔斜率，笔长度]
        分型列表:[high_price, low, dt, direction, index of k_list],index of k_list是属于合并k线的索引
        """
        data=self.stroke_list
        macd_total=0 #macd面积和
        macd_white_max=0
        K_gap=self.get_dt_index(data[end_ix][2])-self.get_dt_index(data[start_ix][2])  #未合并K线间隔
        #计算开始和结束点位价格
        if data[start_ix][3]=='down':
            start_price=data[start_ix][1]
            end_price=data[end_ix][0]
        else:
            start_price=data[start_ix][0]
            end_price=data[end_ix][1]
        
        for i in range(start_ix,end_ix+1):
            if data[i][3]==data[end_ix][3]:#与最后的分型类型相同
                dt=data[i][2]
                fx_strength=self.stroke_strength[dt]
                macd_total+=fx_strength[1]
                if abs(fx_strength[2])>macd_white_max:
                    macd_white_max=abs(fx_strength[2])   
        line_length=round(math.hypot(abs(end_price-start_price),K_gap),2)
        line_gradient=round(math.degrees(math.atan(abs((K_gap)/(end_price-start_price)))),2)
        
        return [macd_total,macd_white_max,line_gradient,line_length]
    
    def cal_lines_macd(self,start_ix,end_ix):
        """
        计算线段内部中枢连接段macd面积和斜率
        data:lines_list
        pattern:'up' or 'down'
        返回强度数据列表[类型，macd面积，macd白线高度，笔斜率，笔长度]
        分型列表:[high_price, low, dt, direction, index of k_list],index of k_list是属于合并k线的索引
        """
        data=self.line_list
        macd_total=0 #macd面积和
        macd_white_max=0
        K_gap=self.get_dt_index(data[end_ix][2])-self.get_dt_index(data[start_ix][2])  #未合并K线间隔
        #计算开始和结束点位价格
        if data[start_ix][3]=='down':
            start_price=data[start_ix][1]
            end_price=data[end_ix][0]
        else:
            start_price=data[start_ix][0]
            end_price=data[end_ix][1]
        
        for i in range(start_ix,end_ix+1):
            if data[i][3]==data[end_ix][3]:#与最后的分型类型相同
                dt=data[i][2]
                fx_strength=self.line_strength[dt]
                macd_total+=fx_strength[1]
                if abs(fx_strength[2])>macd_white_max:
                    macd_white_max=abs(fx_strength[2])   
        line_length=round(math.hypot(abs(end_price-start_price),K_gap),2)
        line_gradient=round(math.degrees(math.atan(abs((K_gap)/(end_price-start_price)))),2)
        
        return [macd_total,macd_white_max,line_gradient,line_length]
    
    #判断线段内部的笔背驰，需要判断三个级别，一个是线段内部扩展成了更高级别的中枢，这种情况非常少，但是也需要考虑
    #本级别中枢判断，还需要分清中枢两边的连接段的形式是什么样，是否应该相应
    #最后就是判断类中枢的笔与笔之间的比较
    #这里需要几个函数，1.当前分型是否是线段内部极点，2.黄白线回抽函数
    def is_line_extreme(self,data): 
        """
        这个函数判断最近的笔分型是否是一个线段内的极点
        data：线段数据
        [pattern,round(sum+addition,1),self.k_macd_list[end][0],line_gradient,line_length]
        """
        if not data:
            return 1
        cur_fx=self.stroke_list[-1]
        cur_line=data[-1]
        cur_line_ix=self.line_index[cur_line[2]]
        flag=1
        
        if ((cur_fx[3]==cur_line[3]=='up' and cur_fx[0]>cur_line[0]) 
            or (cur_fx[3]=='up'!=cur_line[3] and self.stroke_list[-2][2]!=cur_line[2])
           ):
            
            for i in range(cur_line_ix,len(self.stroke_list)):
                if i<len(self.stroke_list)-1 and self.stroke_list[i][0]>cur_fx[0]:
                    flag=0
                    break
        elif cur_fx[3]==cur_line[3]=='up' and cur_fx[0]<=cur_line[0]:
            flag=0
        elif ((cur_fx[3]==cur_line[3]=='down' and cur_fx[1]<cur_line[1]) 
              or (cur_fx[3]=='down'!=cur_line[3] and self.stroke_list[-2][2]!=cur_line[2])):
            for i in range(cur_line_ix,len(self.stroke_list)):
                if i<len(self.stroke_list)-1 and self.stroke_list[i][1]<cur_fx[1]:
                    flag=0
                    break
        elif cur_fx[3]==cur_line[3]=='down' and cur_fx[1]>=cur_line[1]:
            flag=0
            
        return flag  
    
    def is_dea_retreat(self,start,end): 
        """
        dea回抽零轴函数，笔的背驰判断是需要用的函数
        """
        dea_start=self.k_macd_list[start][1]
        dea_end=self.k_macd_list[end][1]
        dea_min=min(abs(dea_start),abs(dea_end))
        retreat=0
        if start>=end:
            return retreat
        for i in self.k_macd_list[start:end]:
            if abs(i[1])<dea_min:
                retreat=1
        return retreat
    
    
    def has_lower_pivot(self,pivot_list,start,end):
        """
        两点之间是否有中枢
        """
        if not pivot_list:
            return 0
        else:
            for p in pivot_list:
                if p[0]>start and p[1]<end:
                    return 1
            return 0
    
    def on_stroke_divergence_signal(self,data):
        """
        基于笔的背驰判断函数
        data:线段数据
        中枢列表：[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]
        背驰判断：1.macd背驰但是不能角度更小长度更长，2.角度更大长度更短
        """
        if len(data)<1:
            return 
        
        higher_lever_divergence=0
        lower_lever_divergence=0
        stroke_lever_divergence=0
        lower_data=self.stroke_list
        
        cur_is_extreme=self.is_line_extreme(data)
        cur_fx=lower_data[-1]
        cur_line=data[-1]
        cur_end_ix=len(lower_data)-1
        if ((cur_fx[3]==cur_line[3]=='down' and cur_fx[1]<cur_line[1]) or
            (cur_fx[3]==cur_line[3]=='up' and cur_fx[0]>cur_line[0])):
            #当前分型超过前一个线段端点
            if len(data)<2:
                return 
            else:
                cur_line=data[-2]
                
        pivot_list=self.stroke_pivot_of_line.get(cur_line[2])
        higher_lever_pivot=[]
        if pivot_list and cur_fx[3]!=cur_line[3]: #如果前面有中枢，那就要看有几个中枢了，有没有高级别中枢
              #高级别中枢序数列表
            for i in range(len(pivot_list)):
                if pivot_list[i][-1]>=9:
                    higher_lever_pivot.append(i)
            if higher_lever_pivot: 
                #提取高级别中枢两边的连接段
                if len(higher_lever_pivot)==1: #只有一个高级别中枢
                    
                    compare_start_ix=self.line_index[cur_line[2]]
                    compare_end_ix=pivot_list[higher_lever_pivot[-1]][0]
                    cur_start_ix=pivot_list[higher_lever_pivot[-1]][1]

                elif len(higher_lever_pivot)>1: #有两个高级别中枢
                    
                    compare_start_ix=pivot_list[higher_lever_pivot[-2]][1]
                    compare_end_ix=pivot_list[higher_lever_pivot[-1]][0]
                    cur_start_ix=pivot_list[higher_lever_pivot[-1]][1]
                #判断高级别中枢两段连接段是否对应
                if (self.has_lower_pivot(pivot_list,compare_start_ix,compare_end_ix)==1!=
                    self.has_lower_pivot(pivot_list,cur_start_ix,cur_end_ix)):
                    
                    return [higher_lever_divergence,lower_lever_divergence,stroke_lever_divergence]
                
                elif (self.has_lower_pivot(pivot_list,compare_start_ix,compare_end_ix)==0
                      and compare_end_ix-compare_start_ix>=3 and cur_end_ix-cur_start_ix==1):
                    
                    if higher_lever_pivot[-1][-1]==9:
                        return [higher_lever_divergence,lower_lever_divergence,stroke_lever_divergence]
                    else:
                        cur_start_ix-=3
                #判断高级别中枢两段连接段的力度    
                cur_strength=self.cal_strokes_macd(cur_start_ix,cur_end_ix)
                compare_strength=self.cal_strokes_macd(compare_start_ix,compare_end_ix)  
                if ((cur_strength[0]<compare_strength[0] and
                     (cur_strength[2]>compare_strength[2] or cur_strength[3]<compare_strength[3])) or 
                    (cur_strength[2]>compare_strength[2] and cur_strength[3]<compare_strength[3])):
                    
                    higher_lever_divergence=1
                else:
                      return [higher_lever_divergence,lower_lever_divergence,stroke_lever_divergence]
                      
             #判断低级别中枢连接段背驰
            if pivot_list[-1][-1]<9 and cur_is_extreme:
                
                compare_end_ix=pivot_list[-1][0]
                cur_start_ix=pivot_list[-1][1]
                
                if len(pivot_list)==1:
                    #一个中枢就以线段开始点作为比较段开始点
                    compare_start_ix=self.line_index[cur_line[2]]
                else: #两个中枢前段比较段开始取倒数第二个中枢的中枢结束点为开始点
                    compare_start_ix=pivot_list[-2][1]
                
                #低级别中枢两段连接段不对应
                if compare_end_ix-compare_start_ix>=3 and cur_end_ix-cur_start_ix==1:
                    if pivot_list[-1][-1]==3:
                        compare_start_ix=pivot_list[-1][0]-1
                    else:
                        cur_start_ix-=3
                #若对应则判断级别
                cur_strength=self.cal_strokes_macd(cur_start_ix,cur_end_ix)
                compare_strength=self.cal_strokes_macd(compare_start_ix,compare_end_ix)     
                if ((cur_strength[0]<compare_strength[0] and
                     (cur_strength[2]>compare_strength[2] or cur_strength[3]<compare_strength[3])) or 
                    (cur_strength[2]>compare_strength[2] and cur_strength[3]<compare_strength[3])):
                    
                    lower_lever_divergence=1   
                else:
                    return [higher_lever_divergence,lower_lever_divergence,stroke_lever_divergence]
                            
        #判断类中枢连接段背驰  
        if cur_is_extreme: #如果无中枢，笔背驰判断
            
            compare_strength=self.stroke_strength[lower_data[-3][2]]
            cur_strength=self.stroke_strength[cur_fx[2]]
            cur_no_inc_ix=self.get_dt_index(cur_fx[2])
            compare_stroke_ix=self.get_dt_index(lower_data[-3][2])
            is_dea_retreat=self.is_dea_retreat(compare_stroke_ix,cur_no_inc_ix) #笔背驰判断黄白线回抽
            #笔背驰：1.macd背驰但是不能角度更小，长度更大，2.角度更大且长度更小
            if ((cur_strength[1]<compare_strength[1] and is_dea_retreat and 
                 (cur_strength[3]>compare_strength[3] or cur_strength[4]<compare_strength[4])) or 
                    (cur_strength[3]>compare_strength[3] and cur_strength[4]<compare_strength[4])):
                
                print("笔比较点",cur_fx[2],lower_data[-3][2])
                print('macd比较',cur_strength[1],compare_strength[1])
                print('角度比较',cur_strength[2],compare_strength[2])
                stroke_lever_divergence=1 
            else:
                stroke_lever_divergence=0
                
        return [higher_lever_divergence,lower_lever_divergence,stroke_lever_divergence]
        
    #线段背驰，由于线段的判断成立需要三个分型，所以当前的分型肯定要比1类买点要高，这就错过了行情，所以需要
    #先行将当前分型判断是否构成线段端点，然后在划分中枢，从而判断背驰，判断完了之后还需要将原有的线段以及中枢列表恢复原样
    #这里用三个函数，一个函数是判断当前笔分型是否可以构成线段端点的函数，还有一个函数是判断当前笔分型是否是线段的极点
    #另一个函数是在新划分线段和中枢的情况下判断背驰，然后恢复远洋
    
    def is_trend_extreme(self,data): 
        """
        这个函数判断某个分型是否是一个线段内的极点
        data：趋势数据trend_list
        [pattern,round(sum+addition,1),self.k_macd_list[end][0],line_gradient,line_length]
        """
        if not data:
            return 1
        cur_fx=self.stroke_list[-1]
        cur_trend=data[-1]
        cur_trend_ix=self.trend_index[cur_trend[2]]
        flag=1

        if ((cur_fx[3]==cur_trend[3]=='up' and cur_fx[0]>cur_trend[0]) 
            or (cur_fx[3]=='up'!=cur_trend[3] and self.line_list[-2][2]!=cur_trend[2])
           ):
            
            for i in range(cur_trend_ix,len(self.line_list)):
                if i<len(self.line_list)-1 and self.line_list[i][0]>cur_fx[0]:
                    flag=0
                    break

        elif cur_fx[3]==cur_trend[3]=='up' and cur_fx[0]<=cur_trend[0]:
            flag=0
        elif ((cur_fx[3]==cur_trend[3]=='down' and cur_fx[1]<cur_trend[1]) 
              or (cur_fx[3]=='down'!=cur_trend[3] and self.line_list[-2][2]!=cur_trend[2])
             ):
            
            for i in range(cur_trend_ix,len(self.line_list)):
                if i<len(self.line_list)-1 and self.line_list[i][1]<cur_fx[1]:
                    flag=0
                    break
                
        elif cur_fx[3]==cur_trend[3]=='down' and cur_fx[1]>=cur_trend[1]:
            flag=0
            
        return flag
    
    
    def stroke_is_line(self,data):
        """
        判断当前笔是否可能成为线段端点：只要满足当前分型与上一个线段点大于3,同时是极点
        分型列表:[high_price, low, dt, direction, index of k_list],index of k_list是属于合并k线的索引
        """
        cur_is_extreme=self.is_line_extreme(data)
        line_ix=self.line_index[data[-1][2]]
        flag=0  #可以成立端点变量：1是可以，0是不可以
        pattern=0 #延续还是新生变量:1是新生，0是延续
        if len(self.stroke_list)-line_ix>=3 and self.stroke_list[-1][3]!=data[-1][3] and cur_is_extreme:
            flag=1
            pattern=1
        elif self.stroke_list[-1][3]==data[-1][3]=='up' and self.stroke_list[-1][0]>data[-1][0] and cur_is_extreme:
            flag=1
        elif self.stroke_list[-1][3]==data[-1][3]=='down' and self.stroke_list[-1][1]<data[-1][1] and cur_is_extreme:
            flag=1
        return [flag,pattern]
    def on_line_divergence_signal(self,data):
        """
        基于线段的背驰判断函数
        data:趋势列表数据trend_list
        中枢列表：[start_ix,end_ix,ZG,ZD,GG,DD,start_dt,end_dt,pivot_length]
        背驰判断：1.macd背驰但是不能角度更小长度更长，2.角度更大长度更短
        """
        if len(data)<1:
            return 
        line_store=[]#旧线段保存变量
        flag=1 #函数进程标志
        higher_lever_divergence=0 #高级别背驰变量
        lower_lever_divergence=0 #本级别背驰变量
        line_lever_divergence=0 #低级别背驰变量
        lower_data=self.line_list
        
        ##############1.重构线段列表
        stroke_is_line=self.stroke_is_line(self.line_list)
        if stroke_is_line[1]==1: #新生端点
            #将当前笔存入线段列表
            self.line_list.append(self.stroke_list[-1])
            self.line_index[self.stroke_list[-1][2]]=len(self.stroke_list)-1
        elif stroke_is_line[1]==0 and stroke_is_line[0]==1: #延伸端点
            last_line=self.line_list.pop() #将线段最后一个端点弹出来保存
            line_store.append(last_line)
            line_store.append(self.line_index[last_line[2]])
            #将当前笔存入线段列表
            self.line_list.append(self.stroke_list[-1])
            self.line_index[self.stroke_list[-1][2]]=len(self.stroke_list)-1
        else:
            return
        
        ###############2.开始判断背驰
        print("趋势背驰判断")
        cur_fx=lower_data[-1]
        cur_trend=data[-1]
        cur_end_ix=len(lower_data)-1

        if ((cur_fx[3]==cur_trend[3]=='down' and cur_fx[1]<cur_trend[1]) or
            (cur_fx[3]==cur_trend[3]=='up' and cur_fx[0]>cur_trend[0])):
            #当前分型超过前一个线段端点
            if len(data)<2:
                flag=0
            else:
                cur_trend=data[-2]
        #重构中枢
        old_pivot=self.line_pivot_of_trend.get(cur_trend[2]) #原来的中枢列表
        self.on_line_pivot_of_trend(self.line_list)
        pivot_list=self.line_pivot_of_trend.get(cur_trend[2]) #新中枢列表
        cur_is_extreme=self.is_trend_extreme(data)
        higher_lever_pivot=[]
        if pivot_list and cur_fx[3]!=cur_trend[3] and flag==1: #如果前面有中枢，那就要看有几个中枢了，有没有高级别中枢
            
              #高级别中枢序数列表
            for i in range(len(pivot_list)):
                if pivot_list[i][-1]>=9:
                    higher_lever_pivot.append(i)
            if higher_lever_pivot: 
                #提取高级别中枢两边的连接段
                if len(higher_lever_pivot)==1: #只有一个高级别中枢
                    
                    compare_start_ix=self.trend_index[cur_trend[2]]
                    compare_end_ix=pivot_list[higher_lever_pivot[-1]][0]
                    cur_start_ix=pivot_list[higher_lever_pivot[-1]][1]

                elif len(higher_lever_pivot)>1: #有两个高级别中枢
                    
                    compare_start_ix=pivot_list[higher_lever_pivot[-2]][1]
                    compare_end_ix=pivot_list[higher_lever_pivot[-1]][0]
                    cur_start_ix=pivot_list[higher_lever_pivot[-1]][1]
                #判断高级别中枢两段连接段是否对应
                if (self.has_lower_pivot(pivot_list,compare_start_ix,compare_end_ix)==1!=
                    self.has_lower_pivot(pivot_list,cur_start_ix,cur_end_ix)):

                    flag=0
                
                elif (self.has_lower_pivot(pivot_list,compare_start_ix,compare_end_ix)==0
                      and compare_end_ix-compare_start_ix>=3 and cur_end_ix-cur_start_ix==1):
                    
                    if higher_lever_pivot[-1][-1]==9:
                        flag=0
                    else:
                        cur_start_ix-=3
                #判断高级别中枢两段连接段的力度    
                cur_strength=self.cal_lines_macd(cur_start_ix,cur_end_ix)
                compare_strength=self.cal_lines_macd(compare_start_ix,compare_end_ix)  
                if ((cur_strength[0]<compare_strength[0] and flag==1 and 
                     (cur_strength[2]>compare_strength[2] or cur_strength[3]<compare_strength[3])) or 
                    (cur_strength[2]>compare_strength[2] and cur_strength[3]<compare_strength[3])):
                    
                    higher_lever_divergence=1
                else:
                      flag=0
                      
            #判断低级别中枢连接段背驰
            
            if pivot_list[-1][-1]<9 and cur_is_extreme and flag==1:
                compare_end_ix=pivot_list[-1][0]
                cur_start_ix=pivot_list[-1][1]
                
                if len(pivot_list)==1:
                    #一个中枢就以线段开始点作为比较段开始点
                    compare_start_ix=self.trend_index[cur_trend[2]]
                else: #两个中枢前段比较段开始取倒数第二个中枢的中枢结束点为开始点
                    compare_start_ix=pivot_list[-2][1]
                
                #低级别中枢两段连接段不对应
                if compare_end_ix-compare_start_ix>=3 and cur_end_ix-cur_start_ix==1:
                    if pivot_list[-1][-1]==3:
                        compare_start_ix=pivot_list[-1][0]-1
                    else:
                        cur_start_ix-=3
                #若对应则判断级别
                cur_strength=self.cal_lines_macd(cur_start_ix,cur_end_ix)
                compare_strength=self.cal_lines_macd(compare_start_ix,compare_end_ix)   

                if ((cur_strength[0]<compare_strength[0] and
                     (cur_strength[2]>compare_strength[2] or cur_strength[3]<compare_strength[3])) or 
                    (cur_strength[2]>compare_strength[2] and cur_strength[3]<compare_strength[3])):
                    
                    lower_lever_divergence=1   
                    
        #判断类中枢连接段背驰  
        if cur_is_extreme: #如果无中枢，笔背驰判断
            
            compare_strength=self.line_strength[lower_data[-3][2]]
            cur_strength=self.line_strength[cur_fx[2]]
            print('当前线段比较',cur_strength,compare_strength)
            #笔背驰：1.macd背驰但是不能角度更小，长度更大，2.角度更大且长度更小
            if ((cur_strength[1]<compare_strength[1]  and 
                 (cur_strength[2]>compare_strength[2] or cur_strength[3]<compare_strength[3])) or 
                    (cur_strength[2]>compare_strength[2] and cur_strength[3]<compare_strength[3])):

                line_lever_divergence=1 
            else:
                line_lever_divergence=0        
                
        ##############3.线段列表复原
        del self.line_index[self.line_list[-1][2]]
        self.line_list.pop()
        if line_store:
            self.line_list.append(line_store[0])
            self.line_index[line_store[0][2]]=line_store[1]
        #############4.复原中枢列表
        if type(old_pivot)==list and old_pivot!=pivot_list:
            self.line_pivot_of_trend[cur_trend[2]]=old_pivot
        elif old_pivot==None and pivot_list!=None:
            del self.line_pivot_of_trend[cur_trend[2]]
        return [higher_lever_divergence,lower_lever_divergence,line_lever_divergence]
        
    def on_buy_sell_signal(self,data):
        """
        这个函数在分笔，段，强度函数之后执行
        data：线段数据
        [pattern,round(sum+addition,1),self.k_macd_list[end][0],line_gradient,line_length]
        
        #################################笔黄白线比较还是有问题还是要判断是否回抽0轴，用dea线判断可能更好一点
        """
        if  len(data)<3:
            return 

        cur_is_extreme=self.is_line_extreme(data)
        cur_fx=self.stroke_list[-1]
        compare_ix=-2 
        if cur_is_extreme:
            compare_stroke=self.stroke_list[-3]  ######################## 延伸还是转折
            cur_fx_ix=self.get_dt_index(cur_fx[2])
            compare_stroke_ix=self.get_dt_index(compare_stroke[2])
            compare_line=self.line_list[-1]  #【-2】为转折，【-3】为延伸
            if cur_fx[3]==compare_line[3]: #若当前分型与前一线段点同类型，则比较的线段点往前移一个
                compare_ix=-3
            else:
                compare_ix=-2
            compare_line=self.line_list[compare_ix]
            cur_stroke_strength=self.stroke_strength[cur_fx[2]]
            compare_stroke_strength=self.stroke_strength[compare_stroke[2]]
            cur_line_strength=self.line_strength[cur_fx[2]]
            if self.line_strength[compare_line[2]]:
                compare_line_strength=self.line_strength[compare_line[2]]
                if (#笔macd
                    (cur_stroke_strength[1]<compare_stroke_strength[1])
                    #笔黄白线回抽
                    and (self.is_dea_retreat(compare_stroke_ix,cur_fx_ix)or
                         (self.k_macd_list[compare_stroke_ix][0]<0 and compare_stroke[3]=='up'))
                    #线段比较
                    and (cur_line_strength[1]<compare_line_strength[1] 
                         or (cur_fx[1]>self.line_list[compare_ix-1][0] and cur_fx[3]=='down')
                        or (cur_fx[0]<self.line_list[compare_ix-1][1] and cur_fx[3]=='up')) #线段macd
                   ):
                    self.buy_sell_signal[cur_fx[2]]=[compare_stroke[2],cur_fx[3],cur_is_extreme,compare_line[2],
                                                     compare_stroke_strength[2],cur_fx[0]]
    
        
    
    
    def  on_first_buy_sell(self,data):
        """
        当前分型是极点且与前一个线段端点间有至少三笔
        当前分型比前一同方向线段高点要高，或者低点要底
        上一个同方向线段比上上一个同方向线段力度要大
        len(self.stroke_list) - self.line_index[str(self.line_list[-1][2])]>2：这就是当前笔与前一线段端点至少3笔
        """      
        if  len(data)<4:
            return 
        last=-1
        
        cur_fx=self.stroke_list[-1]
        pre_fx=self.stroke_list[-2]
        cur_no_inc_ix=self.get_dt_index(cur_fx[2])
        last_no_inc_ix=self.get_dt_index(pre_fx[2])
        cur_is_stroke_extreme=self.is_stroke_extreme(cur_no_inc_ix,last_no_inc_ix,cur_fx)
        cur_is_line_extreme=self.is_line_extreme(data)
        
        if cur_fx[3]=='up':
            buy_sell='sell_1'
        else:
            buy_sell='buy_1'        
        if cur_is_line_extreme and cur_is_stroke_extreme: #当前分型是极点则进入判断一类买卖点
            #最后线段端点
            if cur_fx[3]== self.line_list[last][3]: #分型类型相同,最后一个线段端点前移
                last=-2
                
            if ((cur_fx[3]=='up' and cur_fx[0]>self.line_list[last-1][0] 
                 and self.line_list[last][1]>self.line_list[last-2][1]
                ) 
                or (cur_fx[3]=='down' and cur_fx[1]<self.line_list[last-1][1] 
                    and self.line_list[last][0]<self.line_list[last-2][0]
                   ) 
               ): #当前分型极点大于前一个同类分型且有两个线段中枢,首先分析形态
                
                compare_stroke=self.stroke_list[-3] 
                compare_stroke_ix=self.get_dt_index(compare_stroke[2]) #前笔索引
                cur_stroke_strength=self.stroke_strength[cur_fx[2]] #当前笔强度
                
                compare_line=self.line_list[last-1] #前一段
                compare_stroke_strength=self.stroke_strength[compare_stroke[2]] #前一笔强度
                cur_line_strength=self.line_strength[cur_fx[2]] #当前分型线段强度
                is_dea_retreat=self.is_dea_retreat(compare_stroke_ix,cur_no_inc_ix) #黄白线回抽
                
                #然后开始比较力度
                if self.line_strength[compare_line[2]]:
                    compare_line_strength=self.line_strength[compare_line[2]]
                    if cur_line_strength[2]<compare_line_strength[2] and cur_line_strength[3]>compare_line_strength[3]:
                        
                        pass
                    elif (cur_line_strength[1]<compare_line_strength[1]
                          or (cur_line_strength[2]>compare_line_strength[2] and cur_line_strength[3]<compare_line_strength[3])
                         ): #线段macd背驰
                        
                        if cur_stroke_strength[3]<compare_stroke_strength[3] and cur_stroke_strength[4]>compare_stroke_strength[4]:
                            pass
                        elif (#笔macd
                            (cur_stroke_strength[1]<compare_stroke_strength[1])
                        #笔黄白线回抽
                            and (is_dea_retreat or
                                 (self.k_macd_list[compare_stroke_ix][0]<0 and compare_stroke[3]=='up'))
                            ):
                            self.buy_sell_signal[cur_fx[2]]=[compare_stroke[2],buy_sell,cur_is_line_extreme,
                                                             compare_line[2],compare_stroke_strength[2],cur_fx[0]]
                        elif((cur_fx[3]=='down' and self.stroke_list[-2][0]>=self.stroke_list[-5][1])
                            or (cur_fx[3]=='up' and self.stroke_list[-2][1]<=self.stroke_list[-5][0])
                            ):
                            compare_stroke_pre=self.stroke_list[-5]  #存在笔中枢
                            if cur_stroke_strength[1]<self.stroke_strength[compare_stroke_pre[2]][1]:
                                self.buy_sell_signal[cur_fx[2]]=[compare_stroke[2],buy_sell,cur_is_line_extreme,
                                                                 compare_line[2],compare_stroke_strength[2],cur_fx[0]]
    
    
    
    def on_third_buy_sell(self,data):
        """
        当前分型是极点且与前一个线段端点间有至少三笔
        当前分型比前一同方向线段高点要底，或者低点要高
        上一个同方向线段比上上一个同方向线段力度要小，且创新高新低
        笔强度：[pattern,round(sum+addition,2),abs(self.k_macd_list[end][0]),line_gradient,line_length]
        """
        
        if  len(data)<4:
            return 
        last=-1
        cur_fx=self.stroke_list[-1]
        pre_fx=self.stroke_list[-2]
        cur_no_inc_ix=self.get_dt_index(cur_fx[2])#当前笔索引
        last_no_inc_ix=self.get_dt_index(pre_fx[2])
        cur_is_stroke_extreme=self.is_stroke_extreme(cur_no_inc_ix,last_no_inc_ix,cur_fx)
        cur_is_line_extreme=self.is_line_extreme(data)
        
        if cur_fx[3]=='up':
            buy_sell='sell_3'
        else:
            buy_sell='buy_3'
            
        if cur_is_line_extreme and cur_is_stroke_extreme: #当前分型是极点则进入判断一类买卖点
            #最后线段端点
            if cur_fx[3]== self.line_list[last][3]: #分型类型相同,最后一个线段端点前移
                last=-2
                
            if ((cur_fx[3]=='up' and cur_fx[0]<self.line_list[last-2][1] 
                ) 
                or (cur_fx[3]=='down' and cur_fx[1]>self.line_list[last-2][0] 
                   ) 
                ): #当前分型极点大于前一个同类分型且有两个线段中枢,首先分析形态
                compare_stroke=self.stroke_list[-3] 
                compare_stroke_ix=self.get_dt_index(compare_stroke[2]) #前笔索引
                cur_stroke_strength=self.stroke_strength[cur_fx[2]] #当前笔强度
                
                compare_line=self.line_list[last-1] #前一段
                compare_stroke_strength=self.stroke_strength[compare_stroke[2]] #前一笔强度
                cur_line_strength=self.line_strength[cur_fx[2]] #当前分型线段强度
                is_dea_retreat=self.is_dea_retreat(compare_stroke_ix,cur_no_inc_ix) #黄白线回抽
                
                
                if cur_stroke_strength[3]<compare_stroke_strength[3] and cur_stroke_strength[4]>compare_stroke_strength[4]:
                    #若当前笔的长度比上一笔同方向笔长度长且下跌斜率大
                    pass
                elif (#笔macd
                    (cur_stroke_strength[1]<compare_stroke_strength[1])
                #笔黄白线回抽
                    and (is_dea_retreat or
                         (self.k_macd_list[compare_stroke_ix][0]<0 and compare_stroke[3]=='up'))
                    ):

                    self.buy_sell_signal[cur_fx[2]]=[compare_stroke[2],buy_sell,cur_is_line_extreme,
                                                     compare_line[2],compare_stroke_strength[2],cur_fx[0]]
                elif((cur_fx[3]=='down' and self.stroke_list[-2][0]>=self.stroke_list[-5][1])
                    or (cur_fx[3]=='up' and self.stroke_list[-2][1]<=self.stroke_list[-5][0])
                    ):
                    compare_stroke_pre=self.stroke_list[-5]  #存在笔中枢
                    if cur_stroke_strength[1]<self.stroke_strength[compare_stroke_pre[2]][1]:
                        self.buy_sell_signal[cur_fx[2]]=[compare_stroke[2],buy_sell,cur_is_line_extreme,
                                                         compare_line[2],compare_stroke_strength[2],cur_fx[0]]

       